using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Data;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Text;
using System.Windows.Forms;

namespace Universe.WinForms.ColorPickerUI
{
    public partial class ColorPickerContent : Control
    {
        Size _CellNumber = new Size(40, 12);
        Point _Padding = new Point(4, 4);
        Size _CellSize = new Size(11, 11);
        CellInfo[,] _Cells;
        Point _Selected = Point.Empty;
        bool _IsSelected = false;
        int _Spacing = 1;

        Point? _HoverPoint;

        public ColorPickerContent()
        {
            SetStyle(ControlStyles.Selectable, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);
            SetStyle(ControlStyles.FixedHeight | ControlStyles.FixedWidth, true);
            InitializeComponent();
            this.MouseMove += new MouseEventHandler(ColorPickerContent_MouseMove);
            this.MouseLeave += new EventHandler(ColorPickerContent_MouseLeave);
            this.MouseClick += new MouseEventHandler(ColorPickerContent_MouseClick);
            this.Size = WorkAreaSize;
            this.Resize += new EventHandler(ColorPickerContent_Resize);
        }

        protected override void OnClick(EventArgs e)
        {
            if (IsSelected)
                base.OnClick(EventArgs.Empty);
        }
        
        void ColorPickerContent_Resize(object sender, EventArgs e)
        {
            if (Size != WorkAreaSize)
                Size = WorkAreaSize;
        }

        [Category("Color Picker")]
        public Size CellSize
        {
            get { return _CellSize; }
            set
            {
                if (_CellSize != value)
                {
                    _CellSize = value;

                    _Background = null;
                    OnResize(EventArgs.Empty);
                    
                }
            }
        }

        Point? FindCell(Point cursor)
        {
            for (int y = 0; y < CellNumber.Height; y++)
                for (int x = 0; x < CellNumber.Width; x++)
                {
                    Rectangle rect = Cells[x, y].Rect;
                    rect = new Rectangle(rect.Left, rect.Top, rect.Width + 1, rect.Height + 1);
                    if (rect.Contains(cursor))
                    {
                        return new Point(x, y);
                    }
                }

            return null;
        }

        void ColorPickerContent_MouseClick(object sender, MouseEventArgs e)
        {
            Point? cell = FindCell(e.Location);
            if (cell.HasValue)
            {
                Selected = cell.Value;
                OnClick(EventArgs.Empty);
            }
            
            
        }

        void ColorPickerContent_MouseLeave(object sender, EventArgs e)
        {
            _HoverPoint = null;
        }

        void ColorPickerContent_MouseMove(object sender, MouseEventArgs e)
        {

            Point? cell = FindCell(e.Location);
            HoverPoint = cell;
        }

        private Point? HoverPoint
        {
            get { return _HoverPoint; }
            set
            {
                if (value != _HoverPoint)
                {
                    _HoverPoint = value;
                    Invalidate();
                }
            }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            e.Graphics.DrawImageUnscaled(InteractiveBackground, new Point(0, 0));
            // e.Graphics.DrawRectangle(SystemPens.ControlText, new Rectangle(new Point(0,0), WorkAreaSize));
        }

        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
            // if (keyData == Keys.Enter) return true;
            
            if (keyData == Keys.Space)
            {
                OnClick(EventArgs.Empty);
                return true;
            }
                
            if (keyData == Keys.Left)
            {
                if (_IsSelected && Selected.X > 0)
                {
                    Selected = new Point(Selected.X - 1, Selected.Y);
                    Debug.WriteLine("Selected Changes to " + Selected);
                }
                return true;
            }

            else if (keyData == Keys.Right)
            {
                if (_IsSelected && Selected.X < CellNumber.Width - 1)
                {
                    Selected = new Point(Selected.X + 1, Selected.Y);
                }
                return true;
            }

            else if (keyData == Keys.Up)
            {
                if (_IsSelected && Selected.Y > 0)
                {
                    Selected = new Point(Selected.X, Selected.Y - 1);
                }
                return true;
            }

            else if (keyData == Keys.Down)
            {
                if (_IsSelected && Selected.Y < CellNumber.Height - 1)
                {
                    Selected = new Point(Selected.X, Selected.Y + 1);
                }
                return true;
            }

            return base.ProcessCmdKey(ref msg, keyData);
        }


        [Category("Color Picker")]
        [Description("Location of selected Color")]
        public Point Selected
        {
            get { return _Selected; }
            set
            {
                if (value.X < 0 || value.Y < 0 || value.X >= _CellNumber.Width || value.Y >= _CellNumber.Height)
                    throw new ArgumentOutOfRangeException("value");

                _IsSelected = true;
                if (_Selected != value)
                {
                    _Selected = value;
                    Invalidate();
                    OnSelectedColorChanged(EventArgs.Empty);
                }
            }
        }

        [Category("Color Picker")]
        public bool IsSelected
        {
            get { return _IsSelected; }
            set
            {
                if (_IsSelected && !value)
                {
                    _IsSelected = value;
                    _Selected = Point.Empty;
                    Invalidate();
                }
            }
        }

        [Category("Color Picker")]
        public event EventHandler SelectedColorChanged;
        
        protected virtual void OnSelectedColorChanged(EventArgs e)
        {
            if (SelectedColorChanged != null)
                SelectedColorChanged(this, EventArgs.Empty);
        }

        [Category("Color Picker")]
        public Color SelectedColor
        {
            get
            {
                if (!_IsSelected)
                    return Color.Empty;

                return Cells[Selected.X, Selected.Y].BackColor;
            }
            set
            {
                if (value.IsEmpty)
                {
                    _IsSelected = false;
                    _Selected = Point.Empty;
                }
                else
                    SelectNearestColor(value);
            }
        }


        public void SelectNearestColor(Color select)
        {
            Point p = Point.Empty;
            long d = long.MaxValue;
            ColorInfo ci = new ColorInfo(select);
            float h = ci.Hue, s = ci.Saturation, l = ci.Luminance;
            for (int y = 0; y < _CellNumber.Height; y++)
                for (int x = 0; x < _CellNumber.Width; x++)
                {
                    Color c = Cells[x, y].BackColor;
                    long
                        dr = ((long)select.R - c.R),
                        dg = ((long)select.G - c.G),
                        db = ((long)select.B - c.B);

                    long cd = dr * dr + dg * dg + db * db;
                    if (cd < d)
                    {
                        p = new Point(x, y);
                        d = cd;
                    }
                }

            Selected = p;
        }



        [Category("Color Picker")]
        public Size CellNumber
        {
            get { return _CellNumber; }
            set
            {
                _IsSelected = false;
                _Selected = Point.Empty;
                _CellNumber = value;
                _Background = null;
                OnResize(EventArgs.Empty);
            }
        }


        Bitmap _Background;

        Size WorkAreaSize
        {
            get
            {
                return new Size(
                    this._CellNumber.Width * (this._CellSize.Width + _Spacing) - _Spacing + _Padding.X * 2,
                    this._CellNumber.Height * (this._CellSize.Height + _Spacing) - _Spacing + _Padding.Y * 2);
            }
        }

        protected Bitmap Background
        {
            get
            {
                lock (this)
                    if (_Background == null)
                    {
                        _Cells = new CellInfo[CellNumber.Width, CellNumber.Height];
                        _Background = new Bitmap(WorkAreaSize.Width, WorkAreaSize.Height, PixelFormat.Format32bppPArgb);
                        using (Graphics g = Graphics.FromImage(_Background))
                        {
                            for (int ly = 0; ly < CellNumber.Height; ly++)
                                for (int lx = 0; lx < CellNumber.Width; lx++)
                                {
                                    float h = 360f * lx / CellNumber.Width;
                                    float s = 0.92f - 0.92f * ly / (CellNumber.Height + 0f);
                                    float l = 0.99f;
                                    Color c = new ColorInfo(h, s, l).Color;

                                    Rectangle rect
                                        = new Rectangle(
                                            lx * (_CellSize.Width + _Spacing) + _Padding.X,
                                            ly * (_CellSize.Height + _Spacing) + _Padding.Y,
                                            _CellSize.Width,
                                            _CellSize.Height);


                                    Rectangle clipRect = new Rectangle(_Padding.X, _Padding.Y, WorkAreaSize.Width - _Padding.X * 2, WorkAreaSize.Height - _Padding.Y * 2);
                                    g.SetClip(clipRect);

                                    float s2 = (float)Math.Pow(s, 1.3);
                                    s2 = 0.8f - 0.6f * ly / CellNumber.Height;
                                    float l2 = 0.7f;
                                    // s2 = s > 0.6f ? s - 0.3f : s + 0.3f;
                                    l2 = 0.2f + 0.6f * ly / CellNumber.Height;
                                    Color gridColor = new ColorInfo(h, s2, l2).Color;
                                    CellInfo cell = new CellInfo(rect, c, gridColor);
                                    _Cells[lx, ly] = cell;

                                    g.FillRectangle(new SolidBrush(cell.BackColor), cell.Rect);
                                    g.DrawRectangle(new Pen(cell.BorderColor), cell.GetExtendedRect(1));

                                    // g.DrawLine(nearPen, rect.Left - 1, rect.Top - 1, rect.Left - 1, rect.Bottom + 1);
                                    // g.DrawLine(nearPen, rect.Left - 1, rect.Top - 1, rect.Right + 1, rect.Top - 1);
                                    g.ResetClip();
                                }
                        }
                    }

                return _Background;
            }
        }


        private CellInfo[,] Cells
        {
            get
            {
                if (_Cells == null)
                    Background.ToString();

                return _Cells;
            }
        }

        protected Bitmap InteractiveBackground
        {
            get
            {
                if (!HoverPoint.HasValue && !_IsSelected)
                    return Background;

                Bitmap ret = (Bitmap)Background.Clone();
                using (Graphics g = Graphics.FromImage(ret))
                {

                    if (_HoverPoint.HasValue)
                    {
                        CellInfo hover = Cells[_HoverPoint.Value.X, _HoverPoint.Value.Y];
                        Rectangle rect = hover.GetExtendedRect(1);
                        rect = new Rectangle(rect.Left, rect.Top, rect.Width - 1, rect.Height - 1);
                        Color border = hover.BorderColor;
                        g.DrawLine(new Pen(ControlPaint.Light(border)), rect.Left, rect.Top, rect.Right, rect.Top);
                        g.DrawLine(new Pen(ControlPaint.Light(border)), rect.Left, rect.Top, rect.Left, rect.Bottom);

                        g.DrawLine(new Pen(ControlPaint.Dark(border)), rect.Left, rect.Bottom, rect.Right, rect.Bottom);
                        g.DrawLine(new Pen(ControlPaint.Dark(border)), rect.Right, rect.Top, rect.Right, rect.Bottom);
                    }

                    if (_IsSelected)
                    {
                        CellInfo selected = Cells[Selected.X, Selected.Y];

                        Rectangle borderRect = selected.GetExtendedRect(5);
                        Color
                            c1 = ControlPaint.LightLight(selected.BackColor),
                            c2 = ControlPaint.DarkDark(selected.BackColor);

                        ColorInfo my = new ColorInfo(selected.BackColor);
                        if (my.Luminance > 0.5)
                        {
                            c1 = new ColorInfo(my.Hue, 1-my.Luminance, 0f).Color;
                            c2 = my.Color;
                        }
                        else
                        {
                            c1 = new ColorInfo(my.Hue, my.Luminance, 1.0f).Color;
                            c2 = new ColorInfo(my.Hue, my.Luminance + 0.5f, 1.0f).Color;
                        }

                        c1 = new ColorInfo(my.Hue, 1 - my.Luminance, 0f).Color;
                        c2 = new ColorInfo(my.Hue, 1.9f*Math.Abs(0.5f - my.Luminance), 0f).Color;

                        c1 = new ColorInfo(my.Hue, 1 - my.Luminance, 1f).Color;
                        c2 = Color.White;
                        
                        
                        LinearGradientBrush br = new LinearGradientBrush(borderRect, c1, c2, LinearGradientMode.BackwardDiagonal);
                        g.FillRectangle(br, selected.GetExtendedRect(4));

                        Rectangle rect = selected.GetExtendedRect(2);
                        g.FillRectangle(new SolidBrush(selected.BackColor), rect);

                    }

                    return ret;
                }
            }


        }

        class CellInfo
        {
            public readonly Rectangle Rect;
            public readonly Color BackColor;
            public readonly Color BorderColor;

            public CellInfo(Rectangle rect, Color backColor, Color borderColor)
            {
                Rect = rect;
                BackColor = backColor;
                BorderColor = borderColor;
            }

            public Rectangle GetExtendedRect(int extension)
            {
                return new Rectangle(Rect.Left - extension, Rect.Top - extension, Rect.Width + extension * 2, Rect.Height + extension * 2);
            }
        }
    }
}